package com.example.rbacmanage.util;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import com.alibaba.druid.support.json.JSONUtils;
import com.example.rbacmanage.common.Constant;
import com.example.rbacmanage.property.MinAppProperty;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Component
public class WxUtil {
    private static final String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/stable_token";
    public static final String qrcodeUrl="https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=";

    @Autowired
    private MinAppProperty minAppProperty;
    @Autowired
    private JsapiServiceExtension jsapiServiceExtension;
    /**
     * 小程序配置
     * @param msgDataFormat 微信小程序消息服务器配置的EncodingAESKey
     * @return
     */
    public  WxMaService getWxMaService(String msgDataFormat){
        WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
        config.setAppid(minAppProperty.getAppId());
        config.setSecret(minAppProperty.getAppSecret());
        config.setToken(minAppProperty.getToken());
        config.setAesKey(minAppProperty.getAesKey());
        config.setMsgDataFormat(msgDataFormat);
        WxMaService wxMaService = new WxMaServiceImpl();
        wxMaService.setWxMaConfig(config);
        return wxMaService;
    }

    /**
     * 给指定用户推送服务通知
     * @param openId
     * @param templateId
     * @param msgDataFormat
     * @param orderNumber
     * @param customName
     * @param amount
     * @param number
     * @param time
     */
    public void sendMsg(String openId,String templateId,
                       String msgDataFormat,
                        String orderNumber,String customName,String amount,String number,String time){
        WxMaService wxMaService=getWxMaService(msgDataFormat);
        Map<String,String> map = new HashMap<>();
        map.put("character_string1.DATA",orderNumber);
        map.put("thing2.DATA",customName);
        map.put("amount3.DATA",amount);
        map.put("number4.DATA",number);
        map.put("time5.DATA",time);
        WxMaSubscribeMessage wxMaSubscribeMessage = WxMaSubscribeMessage.builder()
                .toUser(openId)
                .templateId(templateId)
                .page("pages/my/receiveorder").build();
        map.forEach((k,v)->{
            wxMaSubscribeMessage.addData(new WxMaSubscribeMessage.MsgData(k,v));
        });
        try{
            wxMaService.getMsgService().sendSubscribeMsg(wxMaSubscribeMessage);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 获取微信调用接口的access_token
     * @return
     */
    public  String getAccessToken(boolean forceRefresh){
        RedisTemplate redisTemplate= SpringUtils.getBean("redisTemplate");
        if (redisTemplate.hasKey(Constant.ACCESS_TOKEN) && !forceRefresh){
            //判断是否过期,没有过期则返回
            Long expiresTime = (Long) redisTemplate.opsForValue().get(Constant.ACCESS_TOKEN_EXPIRES_IN_TIME);
            if (expiresTime < System.currentTimeMillis()){
                return (String) redisTemplate.opsForValue().get(Constant.ACCESS_TOKEN);
            }
        }
        Map<String,Object> params = new HashMap<>();
        params.put("grant_type","client_credential");
        params.put("appid",minAppProperty.getAppId());
        params.put("secret",minAppProperty.getAppSecret());
        String result = HttpUtils.doPostJsonReturnString(accessTokenUrl, JSONUtils.toJSONString(params),null);
        Map<String,Object> resultMap = (Map<String, Object>) JSONUtils.parse(result);
        if (!resultMap.isEmpty()){
            Integer expiresIn = (Integer) resultMap.get("expires_in");
            Long expiresTime = System.currentTimeMillis()+expiresIn*1000;
            redisTemplate.opsForValue().set(Constant.ACCESS_TOKEN_EXPIRES_IN_TIME,expiresTime);
            redisTemplate.opsForValue().set(Constant.ACCESS_TOKEN,resultMap.get("access_token"));
            return (String) resultMap.get("access_token");
        }
        return null;
    }



    public  String getQrcodeUrl(String content){
        WxUtil wxUtil=new WxUtil();
        String accessToken = wxUtil.getAccessToken(false);
        Map<String,Object> params = new HashMap<>();
        params.put("page","pages/index/index");
        params.put("scene",content);
        params.put("check_path",false);
        //要打开的小程序版本。正式版为 "release"，体验版为 "trial"，开发版为 "develop"。默认是正式版。
        params.put("env_version","develop");
        byte[] result = HttpUtils.doPostJsonReturnByte(qrcodeUrl+accessToken,params);
        return new String(Objects.requireNonNull(Base64.encodeBase64(result)));
    }

    /**
     * 发起下单请求 获取预支付信息
     * @return prepayId 预支付交易会话标识。调起支付时需要使用的参数，
     * prepay_id有效期为2小时，超过2小时，
     * 商户需要使用原下单参数重新请求下单接口，获取新的prepay_id
     * @return PrepayWithRequestPaymentResponse 包含了下单并返回调起支付参数方法。
     */
    public  PrepayWithRequestPaymentResponse startJsapiPayment(Integer money,String menuOrderIds,String openId,String out_trade_no,Long orderId){
        PrepayRequest prepayRequest = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(1);
        prepayRequest.setAmount(amount);
        prepayRequest.setAppid(minAppProperty.getAppId());
        prepayRequest.setMchid(minAppProperty.getMerchantId());
        prepayRequest.setDescription("测试商品");
        prepayRequest.setNotifyUrl("http://124.221.209.30:8083/app/order/pay_notify_url/"+menuOrderIds+"/"+out_trade_no+"/"+orderId);
        prepayRequest.setOutTradeNo(out_trade_no);
        Payer payer = new Payer();
        payer.setOpenid(openId);
        prepayRequest.setPayer(payer);
        PrepayWithRequestPaymentResponse prepayResponse = jsapiServiceExtension.prepayWithRequestPayment(prepayRequest);
        System.out.println(prepayResponse);
        return prepayResponse;
    }


    /**
     * 微信支付订单号查询订单
     * @param transactionId
     * @return
     */
    public  Transaction queryPayOrder(String merchantId,
                                           String transactionId){
        QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
        queryRequest.setMchid(merchantId);
        queryRequest.setTransactionId(transactionId);
        try{
            Transaction result = jsapiServiceExtension.queryOrderById(queryRequest);
            return result;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    /** 商户订单号查询订单 */
    public  Transaction queryOrderByOutTradeNo(String outTradeNo) {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(minAppProperty.getMerchantId());
        request.setOutTradeNo(outTradeNo);
        return jsapiServiceExtension.queryOrderByOutTradeNo(request);
    }
    /**
     * 关闭订单
     * @param outTradeNo
     * @return
     */
    public  void closeOrder(String outTradeNo){
        CloseOrderRequest queryRequest = new CloseOrderRequest();
        queryRequest.setMchid(minAppProperty.getMerchantId());
        queryRequest.setOutTradeNo(outTradeNo);
        jsapiServiceExtension.closeOrder(queryRequest);
    }

    /**
     * 退款订单
     * @param outTradeNo
     * @return
     */
    public  Refund refundOrder(String outTradeNo,Long menuOrderId){
        //请求微信支付相关配置
        Config config =
                new RSAPublicKeyConfig.Builder()
                        .merchantId(minAppProperty.getMerchantId())
                        // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                        .privateKeyFromPath(minAppProperty.getPrivateKeyPath())
                        .publicKeyFromPath(minAppProperty.getPublicKeyPath())
                        .publicKeyId(minAppProperty.getPublicKeyId())
                        .merchantSerialNumber(minAppProperty.getMerchantSerialNumber())
                        .apiV3Key(minAppProperty.getApiV3Key())
                        .build();
        RefundService service = new RefundService.Builder()
                        .config(config)
                        .build();
        CreateRequest request = new CreateRequest();
        request.setNotifyUrl("http://124.221.209.30:8083/background/menuorder/refund_notify_url/"+menuOrderId+"/"+outTradeNo);
        request.setOutTradeNo(outTradeNo);
        request.setOutRefundNo(outTradeNo);
        AmountReq amount = new AmountReq();
        //todo 默认一分钱
        amount.setTotal(1L);
        amount.setRefund(1L);
        amount.setCurrency("CNY");
        request.setAmount(amount);
        // 调用预下单接口
        // 拿到返回内容
        return service.create(request);
    }

    /**
     * 查询退款订单
     * @param outTradeNo
     * @return
     */
    public  Refund queryRefundOrder(String outTradeNo){
        //请求微信支付相关配置
        Config config =
                new RSAPublicKeyConfig.Builder()
                        .merchantId(minAppProperty.getMerchantId())
                        // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                        .privateKeyFromPath(minAppProperty.getPrivateKeyPath())
                        .publicKeyFromPath(minAppProperty.getPublicKeyPath())
                        .publicKeyId(minAppProperty.getPublicKeyId())
                        .merchantSerialNumber(minAppProperty.getMerchantSerialNumber())
                        .apiV3Key(minAppProperty.getApiV3Key())
                        .build();
        RefundService service =
                new RefundService.Builder()
                        .config(config)
                        .build();
        QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
        request.setOutRefundNo(outTradeNo);
        // 拿到返回内容
        return service.queryByOutRefundNo(request);
    }
}
